'''
Created on 01/06/2010

@author: eh01
'''
from datastore.database import DataStore
from datastore.resource import Resource
from datastore.spaces import Spaces
from datastore.uri import Uri
from edais.wsdl_service import WSDLService
from xml.etree import ElementTree
import StringIO
import os
import unittest



class WSDLServiceTest(unittest.TestCase):

    EDAIS_WSDL_FILE_PATH = '../../src/edais/artefacts/wsdl/%s' 
    EDAIS_WSDL_FILE_PATH_WHEN_RUNNING_FROM_ANT = '../src/edais/artefacts/wsdl/%s'
    EDAIS_SCHEMA_FILE_PATH = '../../src/edais/artefacts/schema/%s'
    EDAIS_SCHEMA_FILE_PATH_WHEN_RUNNING_FROM_ANT = '../src/edais/artefacts/schema/%s'
    def setUp(self):
        self.spaces = Spaces()
        self.ds = DataStore(":memory:")
        # don't need a the actual uri if we are not running in the context of the node
        self.wsdl_service = WSDLService(self.spaces, self.ds, Uri('blah'))


    def tearDown(self):
        self.spaces = None
        self.ds = None
        self.wsdl_service = None

    def open_wsdl_file(self, file_name):
        if os.path.isfile(WSDLServiceTest.EDAIS_WSDL_FILE_PATH % file_name):
            edais_msg_file_path = WSDLServiceTest.EDAIS_WSDL_FILE_PATH
        else:
            edais_msg_file_path = WSDLServiceTest.EDAIS_WSDL_FILE_PATH_WHEN_RUNNING_FROM_ANT
        return open(edais_msg_file_path % file_name)
    
    def open_schema_file(self, file_name):
        if os.path.isfile(WSDLServiceTest.EDAIS_SCHEMA_FILE_PATH % file_name):
            edais_msg_file_path = WSDLServiceTest.EDAIS_SCHEMA_FILE_PATH
        else:
            edais_msg_file_path = WSDLServiceTest.EDAIS_SCHEMA_FILE_PATH_WHEN_RUNNING_FROM_ANT
        return open(edais_msg_file_path % file_name)
    
    def test_pre_parse_xml_to_add_namespace_data_to_elements(self):
        expected_a_what = {"a":"http://a", "b":"http://b"}
        expected_a_hello = {"a":"http://a", "b":"http://b"}
        expected_c_world = {"a":"http://a", "b":"http://b", "c":"http://c"}
        expected_b_ed = {"a":"http://a", "b":"http://b", "c":"http://c2"}
        expected_a_another = {"a":"http://a2", "b":"http://b", "c":"http://c2"}
        test_xml = """<?xml version="1.0" encoding="UTF-8"?>
<a:what xmlns:a="http://a" xmlns:b="http://b">
    <a:hello>
        <c:world xmlns:c="http://c">
            <b:ed xmlns:c="http://c2">
                <a:another xmlns:a="http://a2">
                    Test
                </a:another>
            </b:ed>
        </c:world>
    </a:hello>
</a:what>"""
        xml_fo = StringIO.StringIO(test_xml)
        root_element = self.wsdl_service.pre_parse_xml_to_add_namespace_data_to_elements(xml_fo)
        for element in root_element.getiterator():
            if element.tag == "{http://a}what":
                self.assertEqual(expected_a_what, element.ns_map)            
            elif element.tag == "{http://a}hello":
                self.assertEqual(expected_a_hello, element.ns_map)
            elif element.tag == "{http://c}world":
                self.assertEqual(expected_c_world, element.ns_map)
            elif element.tag == "{http://b}ed":
                self.assertEqual(expected_b_ed, element.ns_map)
            elif element.tag == "{http://a2}another":
                self.assertEqual(expected_a_another, element.ns_map)
            else:
                self.fail("Unexpected tag: %s" % element.tag)
                
    def test_parse_xs_import_children(self):
        '''
        test that it only parses and returns import elements of the direct child elements of the
        parent element. It does not return data from the "import_element_4" as it is not a direct 
        child element of the "parent" element
        '''
        import_element_1_namespace, import_element_1_schema_location = 'http://b/', '../b.xsd'
        import_element_2_namespace, import_element_2_schema_location = 'http://c/', '../0/c.xsd'
        import_element_3_namespace, import_element_3_schema_location = 'http://d/', '../x/y/z/d.xsd'
        expected_import_list = [(import_element_1_namespace, import_element_1_schema_location),
                                (import_element_2_namespace, import_element_2_schema_location),
                                (import_element_3_namespace, import_element_3_schema_location)]
        parent_element = ElementTree.Element('{http://a/}Hello')
        import_element_1 = ElementTree.SubElement(parent_element, '{http://www.w3.org/2001/XMLSchema}import')
        import_element_2 = ElementTree.SubElement(parent_element, '{http://www.w3.org/2001/XMLSchema}import')
        import_element_3 = ElementTree.SubElement(parent_element, '{http://www.w3.org/2001/XMLSchema}import')
        import_element_4 = ElementTree.SubElement(import_element_1, '{http://www.w3.org/2001/XMLSchema}import')
        import_element_1.set('namespace', import_element_1_namespace)
        import_element_1.set('schemaLocation', import_element_1_schema_location)
        import_element_2.set('namespace', import_element_2_namespace)
        import_element_2.set('schemaLocation', import_element_2_schema_location)
        import_element_3.set('namespace', import_element_3_namespace)
        import_element_3.set('schemaLocation', import_element_3_schema_location)    
        import_element_4.set('namespace', 'http://what')
        import_element_4.set('schemaLocation', 'nowhere.xsd')
        import_data = self.wsdl_service.parse_xs_import_children(parent_element)
        self.assertEqual(expected_import_list, import_data)
    
    def test_find_message_element_by_name(self):
        message_element_1_name = "message_1"
        message_element_2_name = "message_2"
        definitions_element = ElementTree.Element('{http://schemas.xmlsoap.org/wsdl/}definitions')
        message_element_1 = ElementTree.SubElement(definitions_element, '{http://schemas.xmlsoap.org/wsdl/}message')
        message_element_2 = ElementTree.SubElement(definitions_element, '{http://schemas.xmlsoap.org/wsdl/}message')
        message_element_1.set('name', message_element_1_name)
        message_element_2.set('name', message_element_2_name)
        message_element = self.wsdl_service.find_message_element_by_name("tns:%s" % message_element_1_name, definitions_element)
        self.assertEqual(message_element_1, message_element)
        message_element = self.wsdl_service.find_message_element_by_name("tns:message_3", definitions_element)
        self.assertEqual(None, message_element)
    
    def test_create_type(self):
        type_name = "hello"
        ns_prefix = 'b'
        ns_full = 'http://b'
        ns_map = {'a':'http://a', ns_prefix:ns_full}
        current_element = ElementTree.Element('{http://a}what')
        current_element.ns_map = ns_map
        resource_artefacts ={}
        uri_location = Uri("a/b/c")
        expected_type_resource = Resource(type="term:eplanning/edais/schema/type")
        expected_type_resource.name = type_name
        expected_type_resource.namespace_prefix = ns_prefix
        expected_type_resource.namespace = ns_full
        type_uri = self.wsdl_service.create_type(":".join([ns_prefix, type_name]), current_element, uri_location, resource_artefacts)
        self.assertEqual(uri_location, type_uri)
        self.assertEqual({uri_location:expected_type_resource}, resource_artefacts)
        
    def test_parse_message(self):
        type_name = "hello"
        ns_prefix = 'b'
        ns_full = 'http://b'  
        ns_map = {'a':'http://a', ns_prefix:ns_full}      
        message_element = ElementTree.Element('{http://schemas.xmlsoap.org/wsdl/}message')
        part_element = ElementTree.SubElement(message_element, '{http://schemas.xmlsoap.org/wsdl/}part')
        part_element.set('element', ':'.join([ns_prefix,type_name]))
        part_element.ns_map = ns_map
        uri_location = Uri("a/b/c")
        wsdl_resource_artefacts = {}
        expected_type_resource = Resource(type="term:eplanning/edais/schema/type")
        expected_type_resource.name = type_name
        expected_type_resource.namespace_prefix = ns_prefix
        expected_type_resource.namespace = ns_full        
        type_uri = self.wsdl_service.parse_message(message_element, uri_location, wsdl_resource_artefacts)
        self.assertEqual(uri_location, type_uri)
        self.assertEqual({uri_location:expected_type_resource}, wsdl_resource_artefacts)
    
    def test_parse_attribute(self):
        attribute_name = "what"
        attribute_type = "xs:string"
        attribute_use_1 = "optional"
        attribute_use_2 = "required"
        attribute_default = "eh"
        uri_prefix = Uri("a/b/c")
        expected_attribute_uri_location = uri_prefix + Uri("/".join(["attribute", attribute_name]))
        expected_attribute_type_uri_location = expected_attribute_uri_location + Uri("type")
        
        # test without 'default' attribute
        schema_resource_artefacts = {}
        attribute_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}attribute')
        attribute_element.set('name', attribute_name)
        attribute_element.set('type', attribute_type)
        attribute_element.set('use', attribute_use_1)
        attribute_element.ns_map = {"xs":"http://www.w3.org/2001/XMLSchema"}
        expected_attribute_resource = Resource(type="term:eplanning/edais/schema/attribute")
        expected_attribute_resource.name = attribute_name
        expected_attribute_resource.type = expected_attribute_type_uri_location
        expected_attribute_resource.use = False
        expected_attribute_resource.default = None        
        attribute_uri = self.wsdl_service.parse_attribute(attribute_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_attribute_uri_location, attribute_uri)
        self.assertEqual(expected_attribute_resource, schema_resource_artefacts[attribute_uri])
        self.assertTrue(expected_attribute_type_uri_location in schema_resource_artefacts)
        
        # test without 'use' attribute and with 'default'
        schema_resource_artefacts = {}
        del attribute_element.attrib['use']
        attribute_element.set('default', attribute_default)
        expected_attribute_resource.default = attribute_default
        attribute_uri = self.wsdl_service.parse_attribute(attribute_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_attribute_uri_location, attribute_uri)
        self.assertEqual(expected_attribute_resource, schema_resource_artefacts[attribute_uri])
        self.assertTrue(expected_attribute_type_uri_location in schema_resource_artefacts)
        
        # test without 'use' attribute and with 'default'
        schema_resource_artefacts = {}
        attribute_element.set('use', attribute_use_2)
        attribute_element.set('default', attribute_default)
        expected_attribute_resource.use = True
        attribute_uri = self.wsdl_service.parse_attribute(attribute_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_attribute_uri_location, attribute_uri)
        self.assertEqual(expected_attribute_resource, schema_resource_artefacts[attribute_uri])
        self.assertTrue(expected_attribute_type_uri_location in schema_resource_artefacts)
        
    def test_parse_simple_content_element(self):
        ns_map = {"xs":"http://www.w3.org/2001/XMLSchema"} 
        extension_type = "xs:int"
        attribute_name_1 = "an1"
        attribute_name_2 = "an2"
        attribute_type = "xs:string"
        schema_resource_artefacts = {}
        uri_prefix = Uri("a/b/c")
        expected_simple_content_uri = uri_prefix + Uri("simplecontent")
        expected_attribute_list = [expected_simple_content_uri + Uri('/'.join(["attribute",attribute_name_1])),
                                   expected_simple_content_uri + Uri('/'.join(["attribute",attribute_name_2]))]
        expected_simple_content_extension_uri = expected_simple_content_uri + Uri("extension")
        simple_content_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}simpleContent')
        extension_element = ElementTree.SubElement(simple_content_element, '{http://www.w3.org/2001/XMLSchema}extension')
        extension_element.set("base", extension_type)
        extension_element.ns_map = ns_map
        attribute_element_1 = ElementTree.SubElement(extension_element, '{http://www.w3.org/2001/XMLSchema}attribute')
        attribute_element_2 = ElementTree.SubElement(extension_element, '{http://www.w3.org/2001/XMLSchema}attribute')
        attribute_element_1.set('name', attribute_name_1)
        attribute_element_1.set('type', attribute_type)
        attribute_element_2.set('name', attribute_name_2)
        attribute_element_2.set('type', attribute_type)
        attribute_element_1.ns_map = ns_map
        attribute_element_2.ns_map = ns_map
        expected_simple_content_resource = Resource(type="term:eplanning/edais/schema/simplecontent")
        expected_simple_content_resource.extension = expected_simple_content_extension_uri
        expected_simple_content_resource.attributes = expected_attribute_list
        simple_content_uri = self.wsdl_service.parse_simple_content_element(simple_content_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_simple_content_uri, simple_content_uri)
        self.assertEqual(expected_simple_content_resource, schema_resource_artefacts[simple_content_uri])
    
    def test_parse_element(self):
        ns_map = {"xs":"http://www.w3.org/2001/XMLSchema"} 
        element_name = "yaya"
        element_min_occurs = "1"
        element_max_occurs = "unbounded"
        element_type = "xs:string"
        uri_prefix = Uri("a/b/c")
        expected_element_element_uri = uri_prefix + Uri("/".join(["element", element_name]))
        expected_element_element_type_uri = expected_element_element_uri + Uri("type")
        
        # test defaults for minOccurs and maxOccurs
        schema_resource_artefacts = {}
        element_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}element')
        element_element.set('name', element_name)
        element_element.set('type', element_type)
        element_element.ns_map = ns_map
        expected_element_resource = Resource(type="term:eplanning/edais/schema/element")
        expected_element_resource.name = element_name
        expected_element_resource.type = expected_element_element_type_uri
        expected_element_resource.min_occurs = 1
        expected_element_resource.max_occurs = 1
        element_uri = self.wsdl_service.parse_element(element_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_element_element_uri, element_uri)
        self.assertEqual(expected_element_resource, schema_resource_artefacts[element_uri])
        
        # test assigning minOccurs and maxOccurs
        schema_resource_artefacts = {}
        element_element.set('minOccurs', element_min_occurs)
        element_element.set('maxOccurs', element_max_occurs)
        expected_element_resource.min_occurs = int(element_min_occurs)
        expected_element_resource.max_occurs = -1
        element_uri = self.wsdl_service.parse_element(element_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_element_element_uri, element_uri)
        self.assertEqual(expected_element_resource, schema_resource_artefacts[element_uri])
    
    def test_parse_complex_type_element(self):
        ns_map = {"xs":"http://www.w3.org/2001/XMLSchema"}
        complex_type_name = "verycomplex"
        
        uri_prefix = Uri("a/b/c")
        expected_complex_type_uri = uri_prefix + Uri("/".join(["complextype", complex_type_name]))
        
        # test simple content and attributes
        attribute_name_1 = "an1"
        attribute_name_2 = "an2"
        attribute_type = "xs:string"
        extension_type = "xs:int"
        expected_complex_type_simple_type_uri = expected_complex_type_uri + Uri("simplecontent")        
        schema_resource_artefacts = {}
        complex_type_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}complexType')
        complex_type_element.set('name', complex_type_name)
        simple_content_element = ElementTree.SubElement(complex_type_element, '{http://www.w3.org/2001/XMLSchema}simpleContent')
        extension_element = ElementTree.SubElement(simple_content_element, '{http://www.w3.org/2001/XMLSchema}extension')
        extension_element.set("base", extension_type)
        extension_element.ns_map = ns_map
        attribute_element_1 = ElementTree.SubElement(complex_type_element, '{http://www.w3.org/2001/XMLSchema}attribute')
        attribute_element_2 = ElementTree.SubElement(complex_type_element, '{http://www.w3.org/2001/XMLSchema}attribute')
        attribute_element_1.set('name', attribute_name_1)
        attribute_element_1.set('type', attribute_type)
        attribute_element_2.set('name', attribute_name_2)
        attribute_element_2.set('type', attribute_type)
        attribute_element_1.ns_map = ns_map
        attribute_element_2.ns_map = ns_map        
        expected_complex_type_resource = Resource(type="term:eplanning/edais/schema/complextype")
        expected_complex_type_resource.name = complex_type_name
        expected_complex_type_resource.simple_content = expected_complex_type_simple_type_uri
        expected_complex_type_resource.elements = []
        expected_complex_type_resource.attributes = [expected_complex_type_uri + Uri('/'.join(['attribute', attribute_name_1])),
                                                     expected_complex_type_uri + Uri('/'.join(['attribute', attribute_name_2]))]     
        complex_type_uri = self.wsdl_service.parse_complex_type_element(complex_type_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_complex_type_uri, complex_type_uri)
        self.assertEqual(expected_complex_type_resource, schema_resource_artefacts[complex_type_uri])
        
        # test non simple content
        element_1_name = "1"
        element_2_name = "2"
        element_3_name = "3"
        element_4_name = "4"
        schema_resource_artefacts = {}
        complex_type_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}complexType')
        complex_type_element.set('name', complex_type_name)
        sequence_element = ElementTree.SubElement(complex_type_element, '{http://www.w3.org/2001/XMLSchema}sequence')
        element_element_1 = ElementTree.SubElement(sequence_element, '{http://www.w3.org/2001/XMLSchema}element')
        element_element_2 = ElementTree.SubElement(sequence_element, '{http://www.w3.org/2001/XMLSchema}element')
        choice_element = ElementTree.SubElement(sequence_element, '{http://www.w3.org/2001/XMLSchema}choice')
        element_element_3 = ElementTree.SubElement(choice_element, '{http://www.w3.org/2001/XMLSchema}element')
        element_element_4 = ElementTree.SubElement(choice_element, '{http://www.w3.org/2001/XMLSchema}element')
        element_element_1.set('name', element_1_name)
        element_element_1.set('type', "xs:int")
        element_element_2.set('name', element_2_name)
        element_element_2.set('type', "xs:int")
        element_element_3.set('name', element_3_name)
        element_element_3.set('type', "xs:int")        
        element_element_4.set('name', element_4_name)
        element_element_4.set('type', "xs:int")                   
        element_element_1.ns_map = ns_map
        element_element_2.ns_map = ns_map
        element_element_3.ns_map = ns_map
        element_element_4.ns_map = ns_map
        expected_complex_type_resource = Resource(type="term:eplanning/edais/schema/complextype")
        expected_complex_type_resource.name = complex_type_name
        expected_complex_type_resource.elements = [expected_complex_type_uri + Uri('/'.join(['element', element_1_name])),
                                                   expected_complex_type_uri + Uri('/'.join(['element', element_2_name])),
                                                   expected_complex_type_uri + Uri('/'.join(['element', element_3_name])),
                                                   expected_complex_type_uri + Uri('/'.join(['element', element_4_name]))]
        expected_complex_type_resource.attributes = []     
        complex_type_uri = self.wsdl_service.parse_complex_type_element(complex_type_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_complex_type_uri, complex_type_uri)
        self.assertEqual(expected_complex_type_resource, schema_resource_artefacts[complex_type_uri])
    
    def test_parse_simple_type_element(self):
        ns_map = {"xs":"http://www.w3.org/2001/XMLSchema"}
        uri_prefix = Uri("a/b/c")        
        simple_type_name = 'verysimple'
        restriction_type = "xs:string"
        enumeration_element_1_value = 'lala'
        enumeration_element_2_value = 'baba'
        expected_simple_type_uri = uri_prefix + Uri('/'.join(["simpletype", simple_type_name]))
        expected_simple_type_restriction_uri = expected_simple_type_uri + Uri('restriction')
        schema_resource_artefacts = {}
        simple_type_element = ElementTree.Element('{http://www.w3.org/2001/XMLSchema}simpleType')
        restriction_element = ElementTree.SubElement(simple_type_element, '{http://www.w3.org/2001/XMLSchema}restriction')
        enumeration_element_1 = ElementTree.SubElement(restriction_element, '{http://www.w3.org/2001/XMLSchema}enumeration')
        enumeration_element_2 = ElementTree.SubElement(restriction_element, '{http://www.w3.org/2001/XMLSchema}enumeration')
        simple_type_element.set('name', simple_type_name)
        restriction_element.set('base', restriction_type)
        enumeration_element_1.set('value', enumeration_element_1_value)
        enumeration_element_2.set('value', enumeration_element_2_value)
        restriction_element.ns_map = ns_map
        expected_simple_type_resource = Resource(type="term:eplanning/edais/schema/simpletype")
        expected_simple_type_resource.name = simple_type_name
        expected_simple_type_resource.restriction = expected_simple_type_restriction_uri
        expected_simple_type_resource.enums = {enumeration_element_1_value:enumeration_element_1_value, 
                                               enumeration_element_2_value:enumeration_element_2_value}
        simple_type_uri = self.wsdl_service.parse_simple_type_element(simple_type_element, uri_prefix, schema_resource_artefacts)
        self.assertEqual(expected_simple_type_uri, simple_type_uri)
        self.assertEqual(expected_simple_type_resource, schema_resource_artefacts[simple_type_uri])

    def test_generate_wsdl_resource_artefacts(self):
        wsdl_rs = Resource(type="term:eplanning/edais/wsdl")
        wsdl_rs_fo = self.open_wsdl_file('QueryPublicStatusApplication_Responder.2.3.0r2.xml')
        wsdl_rs.deserialise(wsdl_rs_fo.read())
        wsdl_resource_artefacts_dict = self.wsdl_service.generate_wsdl_resource_artefacts(wsdl_rs)
        self.assertNotEqual(wsdl_resource_artefacts_dict, None)
        
    def test_generate_schema_resource_artefacts(self):
        # There are 3 different types of schema files used by eDAIS. Since there is not 
        # one schema file that contains all the valid schema structures used in eDAIS. I've 
        # chosen three different schema files to test our parser can parse all the different
        # types.
        
        # test message schema. test parsing elements and complex types
        schema_rs = Resource(type="term:eplanning/edais/schema")
        schema_rs_fo = self.open_schema_file('QueryRequestPublicStatusApplicationQuery.msg.2.3.0r2.xml')
        schema_rs.deserialise(schema_rs_fo.read())
        schema_resource_artefacts_dict = self.wsdl_service.generate_schema_resource_artefacts(schema_rs)
        self.assertNotEqual(schema_resource_artefacts_dict, None)
        
        # test high level data structures. test parsing complex types
        schema_rs = Resource(type="term:eplanning/edais/schema")
        schema_rs_fo = self.open_schema_file('da.data.2.3.0r2.xml')
        schema_rs.deserialise(schema_rs_fo.read())
        schema_resource_artefacts_dict = self.wsdl_service.generate_schema_resource_artefacts(schema_rs)
        self.assertNotEqual(schema_resource_artefacts_dict, None)
        
        # test low level data structures. tests parsing simple types and complex types than have simple content
        schema_rs = Resource(type="term:eplanning/edais/schema")
        schema_rs_fo = self.open_schema_file('cdt.type.2.3.0r2.xml')
        schema_rs.deserialise(schema_rs_fo.read())
        schema_resource_artefacts_dict = self.wsdl_service.generate_schema_resource_artefacts(schema_rs)
        self.assertNotEqual(schema_resource_artefacts_dict, None)

    def test_get_edais_artefacts_path(self):
        uri = Uri('external:edais/artefacts/bcv/.././biv/bim/queryPublicStatusApplication/QueryRequestPublicStatusApplicationQuery.msg.2.3.0r2.xsd')
        expected = 'biv/bim/queryPublicStatusApplication/QueryRequestPublicStatusApplicationQuery.msg.2.3.0r2.xsd'
        edais_artefacts_path = self.wsdl_service.get_edais_artefacts_path(uri)
        self.assertEqual(expected, edais_artefacts_path)
        
if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()